#pragma once 

#include <iostream>
#include <string>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "Log.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"
#include "daemon.hpp"

using namespace std;

extern Log log;
const string defaultip = "0.0.0.0";
const int defaultfd = -1;
const uint16_t defaultport = 8888;
const int backlog = 10;

enum
{
    SOCKET_ERR = 1,
    BIND_ERR,
    LISTEN_ERR,
    USAGE_ERR
};

class TcpServer;

class ThreadDate
{
public:
    ThreadDate(int fd, const std::string &ip, const uint16_t port, TcpServer *t)
        : sockfd(fd)
        , clientip(ip)
        , clientport(port)
        , tcps(t)
    {}

public:
    int sockfd;
    string clientip;
    uint16_t clientport;
    TcpServer* tcps;
};

class TcpServer
{
public:
    TcpServer(uint16_t serverport = defaultport, string serverip = defaultip)
        : _listensock(defaultfd)
        , _serverip(defaultip)
        , _serverport(defaultport)
    {}

    void Init()
    {
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            log(Fatal, "create socket, errno: %d, errstring: %s", errno, strerror(errno));
            exit(SOCKET_ERR);
        }
        log(Info, "create socket success, listensock: %d", _listensock);

        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(_serverport);
        inet_aton(_serverip.c_str(), &(local.sin_addr));
        // local.sin_addr.s_addr = INADDR_ANY;

        if(bind(_listensock, (const struct sockaddr*)&local, sizeof(local)) < 0)
        {
            log(Fatal, "bind error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind socket success, listensock: %d", _listensock);

        if(listen(_listensock, backlog) < 0)
        {
            log(Fatal, "listen error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(LISTEN_ERR);
        }
        log(Info, "listen socket success, listensock: %d", _listensock);
    }

    void Run()
    {
        Daemon();
        ThreadPool<Task>::GetInstance()->Start();
        log(Info, "TcpServer is running...");
        // signal(SIGCHLD, SIG_IGN);
        for(;;)
        {
            // 1. 获取新连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(_listensock, (struct sockaddr*)&client, &len);
            if(sockfd < 0)
            {
                log(Warning, "accept error, errno: %d, errstring: %s", errno, strerror(errno));
                continue;
            }

            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 2. 根据新连接来进行通信
            log(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d", 
                sockfd, clientip, clientport);
            
            // version 1 -- 单进程版
            // Service(sockfd, clientip, clientport);
            // close(sockfd);

            // version 2 -- 多进程版
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     close(_listensock);
            //     if(fork() > 0)
            //         exit(0);
            //     Service(sockfd, clientip, clientport);
            //     close(sockfd);
            //     exit(0);
            // }
            // pid_t rid = waitpid(id, nullptr, 0);

            // version 3 -- 多线程版本
            // ThreadDate* td = new ThreadDate(sockfd, clientip, clientport, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);

            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }

    // static void* Routine(void* args)
    // {
    //     pthread_detach(pthread_self());
    //     ThreadDate* td = static_cast<ThreadDate*>(args);
    //     td->tcps->Service(td->sockfd, td->clientip, td->clientport);
    //     delete td;
    //     return nullptr;
    // }

    // void Service(int sockfd, const string& clientip, const uint16_t clientport)
    // {
    //     char buffer[4096];
    //     while(true)
    //     {
    //         ssize_t n = read(sockfd, buffer, sizeof(buffer));
    //         if(n > 0)
    //         {
    //             buffer[n] = 0;
    //             cout << "client say# " << buffer << endl;
    //             string echo_string = "tcpserver say# ";
    //             echo_string += buffer;

    //             write(sockfd, echo_string.c_str(), echo_string.size());
    //         }
    //         else if (n == 0)
    //         {
    //             log(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
    //             break;
    //         }
    //         else
    //         {
    //             log(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", 
    //                 sockfd, clientip.c_str(), clientport);
    //             break;
    //         }
    //     }
    // }

    ~TcpServer()
    {}

private:
    int _listensock;
    string _serverip;
    uint16_t _serverport;
};
